﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using Emgu.CV;
using Emgu.CV.Structure;

namespace BKBIS
{
    //
    /// <summary>
    /// Lớp này truy xuất vào ảnh và thực hiện các thao tác kiểm tra
    /// </summary>
    internal class ImageProcessing
    {
        /// <summary>
        /// Hàm này kiểm tra hình dạng viên thuốc hình tròn
        /// </summary>
        /// <param name="GrayImageofRound">ảnh đầu vào</param>
        /// <param name="sh">biến hình dạng (shape)</param>
        /// <param name="true_size">biến kích thước</param>
        /// <param name="tolerance_radius">sai số bán kính</param>
        /// <param name="data">dữ liệu viên thuốc</param>
        /// <param name="thresh"></param>
        /// <param name="thresholdvalue"></param>
        public void CheckShape(Image<Gray, Byte> GrayImageofRound, out bool sh,out bool true_size,int tolerance_radius,BlisterFeatures data, bool thresh, int thresholdvalue)
        {
            CircleF[] circle;
            Filter C_Filter = new Filter();
            if (thresh)     
            {
                circle = C_Filter.CannyFilter(GrayImageofRound, thresholdvalue, 30);    // 30 là gì ?
            }
            else
            {
                int range_h = (data.TabletColor.mau_hsv.high_h - data.TabletColor.mau_hsv.low_h)/2;
                int range_y = (data.TabletColor.mau_ycc.high_y - data.TabletColor.mau_ycc.low_y) / 2;
                circle = C_Filter.CannyFilter(GrayImageofRound,
                    Math.Max(data.TabletColor.mau_hsv.H, data.TabletColor.mau_ycc.Y),
                    Math.Max(range_h, range_y) );
            }
            
            if (circle.Count() == 0)    // 
            {
                sh = false;
                true_size = false;
            }
            else  // nếu là hình tròn
            {
                sh = true;  //biến hình dạng = true
                if (2 * circle[0].Radius < data.TabletWidth * (100 - tolerance_radius) / 100 )  //cirle[0] là cái gì?
                {
                    true_size = false;      // nếu kích thước viên thuốc < kích thước training, biến kích thước = false
                }
                else
                {
                    true_size = true;
                }
                
            }
        }

        /// <summary>
        /// hàm này kiểm tra tình trạng diện tích viên thuốc
        /// </summary>
        /// <param name="ImageInput">ảnh xám của ImageROI</param>
        /// <param name="true_area">trả về diện tích viên thuốc</param>
        /// <param name="data">biến chứa thông tin viên thuốc</param>
        /// <param name="tolerance_area">sai số diện tích lấy từ người dùng</param>
        public void CheckArea(Image<Gray, Byte> ImageInput,out int true_area, BlisterFeatures data,
            int tolerance_area)
        {

            int area = ImageInput.CountNonzero()[0];        // đếm số pixel khác không trong ImageROI (tức là đếm số pixel của viên thuốc)
            if (area < data.TabletArea*tolerance_area/100)
            {
                true_area = 0;      // viên thuốc khác màu
            }
            else if (area >= data.TabletArea * tolerance_area / 100 && area <= data.TabletArea * (100 - tolerance_area) / 100 )
            {
                true_area = 1;      // cần kiểm tra thêm biến kích thước, nếu = TRUE --> viên thuốc bị vỡ, nếu = FALSE --> viên thuốc khác kích thước
            }
            else //if (area >= data.SoDiemAnh*(100 - tolerance_area)/100)
            {
                true_area = 2;      // viên thuốc không lỗi
            }
            
            //true_size = 0;
            //Contour<Point> _contours = ImageInput.FindContours(
            //    Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
            //    Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST);

            //while (_contours != null)
            //{
            //    if (_contours.Area > data.SoDiemAnh/20 && _contours.Area < data.SoDiemAnh*2)
            //    {
            //        true_size = 1;
                    
            //            if (Math.Max(_contours.BoundingRectangle.Width, _contours.BoundingRectangle.Height) >
            //                data.KichThuoc_Dai*(100 - tolerance_radius)/100)
            //            {
            //                true_size = 2;
            //                if (Math.Min(_contours.BoundingRectangle.Width, _contours.BoundingRectangle.Height) >
            //                data.KichThuoc_Dai * (100 - tolerance_radius) / 100)
            //                {
            //                    true_size = 3;
            //                }
                        
            //        }
            //    }
            //   _contours= _contours.HNext;

            //}
        }

        /// <summary>
        /// Hàm này dò tìm các điểm thỏa yêu cầu màu sắc cho trước
        /// </summary>
        /// <param name="ImageInputhsv"> ảnh đầu vào </param>
        /// <param name="channel1">giá trị trung bình của h trong hsv</param>
        /// <param name="channel2">giá trị trung bình của s trong hsv</param>
        /// <param name="range_channel1">tầm giá trị h</param>
        /// <param name="range_channel2">tầm giá trị s</param>
        /// <returns> kết quả là các điểm thỏa yêu cầu</returns>
      /*  public void ColorInrange(bool hsv, bool ycc, Image<Hsv, Byte> ImageInputhsv, Image<Ycc, Byte> ImageInputycc,
            out Image<Gray, Byte> _grayImage2, int channel1, int channel2, int channel3, int range_channel1,
            int range_channel2, int range_channel3)
        {
            //ThongTinAnh ImageDatabase = new ThongTinAnh();
            _grayImage2 = new Image<Gray, Byte>(ImageInputhsv.Width, ImageInputhsv.Height);
            if (hsv)
            {
                _grayImage2 =
                    ImageInputhsv.InRange(
                        new Hsv(channel1 - range_channel1, channel2 - range_channel2, channel3 - range_channel3),
                        new Hsv(channel1 + range_channel1, channel2 + range_channel2, channel3 + range_channel3));
            }
            else if (ycc)
            {
                _grayImage2 =
                    ImageInputycc.InRange(
                        new Ycc(channel1 - range_channel1, channel2 - range_channel2, channel3 - range_channel3),
                        new Ycc(channel1 + range_channel1, channel2 + range_channel2, channel3 + range_channel3));
            }

        }
        */

        public void ColorInrange(bool hsv, bool ycc, Image<Hsv, Byte> ImageInputhsv, Image<Ycc, Byte> ImageInputycc,
            out Image<Gray, Byte> _grayImage2, int channel1, int channel2, int channel3, int low_h, int low_s, int low_v,
            int high_h, int high_s, int high_v)
        {
            _grayImage2 = new Image<Gray, Byte>(ImageInputhsv.Width, ImageInputhsv.Height);

            if (hsv)
            {
                _grayImage2 =
                    ImageInputhsv.InRange(
                        new Hsv(low_h, low_s, low_v),
                        new Hsv(high_h, high_s, high_v));
            }
            else if (ycc)
            {
                _grayImage2 =
                    ImageInputycc.InRange(
                        new Ycc(low_h, low_s, low_v),
                        new Ycc(high_h, high_s, high_v));
            }

        }
        /// <summary>
        /// Hàm này kiểm tra tình trạng hình dạng và kích thước viên thuốc ellipse
        /// Sử dụng 3 hàm chính: AbsDiff, ThresholdBinary, DrawContourEllipse, CheckArea
        /// </summary>
        /// <param name="GrayImageInput">ảnh đầu vào dạng gray </param>
        /// <param name="BgrImageInput">ảnh đầu vào dạng bgr </param>
        /// <param name="sh">tình trạng hình dạng viên thuốc </param>
        /// <param name="ra"> tình trạng kích thước viên thuốc</param>
        /// <param name="data">thông tin của quá trình training được lưu trữ</param>
        /// 
        public void check_shape_ellipse(Image<Gray, Byte> gray, Image<Gray, Byte> gray_bin, out bool sta_shape, out bool sta_size, out int sta_area, int tolerance_size, int tolerance_area, BlisterFeatures data, out Rectangle rect_ellipse, int threshvalue)
        {
            // tính average_intensive để đưa vào hàm AbsDiff
            int intensity1 = (int)gray[gray.Height - 1, gray.Width - 1].Intensity;
            int intensity2 = (int)gray[gray.Height - 1, 1].Intensity;
            int intensity3 = (int)gray[1, gray.Width - 1].Intensity;
            int intensity4 = (int)gray[1, 1].Intensity;
            int average_intensity;
            if (Math.Max(intensity1, Math.Max(intensity2, Math.Max(intensity3, intensity4))) <
                Math.Min(intensity1, Math.Min(intensity2, Math.Min(intensity3, intensity4))) + 30)
            {
                average_intensity = (intensity1 + intensity2 + intensity3 + intensity4) / 4;
            }
            else
            {
                average_intensity = (Math.Max(intensity1, Math.Max(intensity2, Math.Min(intensity3, intensity4))) +
                                     Math.Max(intensity1, Math.Min(intensity2, Math.Min(intensity3, intensity4)))) / 2;
            }

            // dùng hàm AbsDiff làm nổi biên
            Image<Gray, Byte> gray1 = gray.AbsDiff(new Gray(average_intensity));

            // dùng hàm ThresholdBinary lấy ngưỡng chuyển sang nhị phân
            Image<Gray, Byte> gray2 = gray1.ThresholdBinary(new Gray(threshvalue), new Gray(255));

            Train train1 = new Train();
            Image<Hsv, Byte> hsv1 = new Image<Hsv, byte>(gray.Width, gray.Height);
            Image<Hsv, Byte> hsv2 = hsv1.Copy();
            Rectangle[] rect1 = new Rectangle[3];
            Rectangle rect2 = new Rectangle(0, 0, gray.Width, gray.Height);

            // dùng hàm DrawContourEllipse vẽ biên 
            train1.DrawContourEllipse(gray2, hsv1, out hsv2, 1, out rect1, rect2, rect2);
            sta_size = false;
            if (rect1[1].Width != 0)
            {
                sta_shape = true;

                if (rect1[1].Width > data.TabletHeight * (100 - tolerance_size) / 100)
                {
                    if (rect1[1].Height > data.TabletWidth * (100 - tolerance_size) / 100)
                    {
                        sta_size = true;
                    }
                }
            }
            else
            {
                sta_shape = false;
            }
            rect_ellipse = rect1[1];

            CheckArea(gray_bin, out sta_area, data, tolerance_area);

            // train1.DrawContourEllipse(gray1,hsv1,out hsv2,1,out rect1, rect2);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="gray">ảnh xám của imageROI</param>
        /// <param name="gray_bin">giống như gray</param>
        /// <param name="sta_shape"></param>
        /// <param name="sta_size"></param>
        /// <param name="sta_area"></param>
        /// <param name="tolerance_size"></param>
        /// <param name="tolerance_area"></param>
        /// <param name="data"></param>
        /// <param name="rect_square"></param>
        /// <param name="threshvalue"></param>
        public void check_shape_square(Image<Gray, Byte> gray, Image<Gray, Byte> gray_bin, out bool sta_shape, out bool sta_size, out int sta_area, int tolerance_size, int tolerance_area, BlisterFeatures data, out Rectangle rect_square, int threshvalue)
        {
            int intensity1 = (int)gray[gray.Height - 1, gray.Width - 1].Intensity;
            int intensity2 = (int)gray[gray.Height - 1, 1].Intensity;
            int intensity3 = (int)gray[1, gray.Width - 1].Intensity;
            int intensity4 = (int)gray[1, 1].Intensity;
            int average_intensity;
            if (Math.Max(intensity1, Math.Max(intensity2, Math.Max(intensity3, intensity4))) <
                Math.Min(intensity1, Math.Min(intensity2, Math.Min(intensity3, intensity4))) + 30)
            {
                average_intensity = (intensity1 + intensity2 + intensity3 + intensity4) / 4;
            }
            else
            {
                average_intensity = (Math.Max(intensity1, Math.Max(intensity2, Math.Min(intensity3, intensity4))) +
                                     Math.Max(intensity1, Math.Min(intensity2, Math.Min(intensity3, intensity4)))) / 2;
            }

            // dùng hàm AbsDiff làm nổi biên
            Image<Gray, Byte> gray1 = gray.AbsDiff(new Gray(average_intensity));

            // dùng hàm ThresholdBinary lấy ngưỡng chuyển sang nhị phân
            Image<Gray, Byte> gray2 = gray1.ThresholdBinary(new Gray(threshvalue), new Gray(255));

            Train train1 = new Train();
            Image<Hsv, Byte> hsv1 = new Image<Hsv, byte>(gray.Width, gray.Height);
            Image<Hsv, Byte> hsv2 = hsv1.Copy();
            Rectangle[] rect1 = new Rectangle[3];       // tại sao khai báo mảng 3 hình chữ nhật
            Rectangle rect2 = new Rectangle(0, 0, gray.Width, gray.Height);

            // dùng hàm DrawContourSquare vẽ biên 
            train1.DrawContourSquare(gray2, hsv1, out hsv2, 1, out rect1, rect2, rect2);

            sta_size = false;

            if (rect1[1].Width != 0)
            {
                sta_shape = true;

                if (rect1[1].Width > data.TabletHeight * (100 - tolerance_size) / 100)
                {
                    if (rect1[1].Height > data.TabletWidth * (100 - tolerance_size) / 100)
                    {
                        sta_size = true;
                    }
                }
            }
            else
            {
                sta_shape = false;
            }
            rect_square = rect1[1];

            CheckArea(gray_bin, out sta_area, data, tolerance_area);


        }
        public void check_shape_capsule(Image<Gray, Byte> gray, Image<Gray, Byte> gray_bin, out bool sta_shape, out bool sta_size, out int sta_area, int tolerance_size, int tolerance_area, BlisterFeatures data, out Rectangle rect_square, int threshvalue)
        {
            int intensity1 = (int)gray[gray.Height - 1, gray.Width - 1].Intensity;
            int intensity2 = (int)gray[gray.Height - 1, 1].Intensity;
            int intensity3 = (int)gray[1, gray.Width - 1].Intensity;
            int intensity4 = (int)gray[1, 1].Intensity;
            int average_intensity;
            if (Math.Max(intensity1, Math.Max(intensity2, Math.Max(intensity3, intensity4))) <
                Math.Min(intensity1, Math.Min(intensity2, Math.Min(intensity3, intensity4))) + 30)
            {
                average_intensity = (intensity1 + intensity2 + intensity3 + intensity4) / 4;
            }
            else
            {
                average_intensity = (Math.Max(intensity1, Math.Max(intensity2, Math.Min(intensity3, intensity4))) +
                                     Math.Max(intensity1, Math.Min(intensity2, Math.Min(intensity3, intensity4)))) / 2;
            }

            // dùng hàm AbsDiff làm nổi biên
            Image<Gray, Byte> gray1 = gray.AbsDiff(new Gray(average_intensity));

            // dùng hàm ThresholdBinary lấy ngưỡng chuyển sang nhị phân
            Image<Gray, Byte> gray2 = gray1.ThresholdBinary(new Gray(threshvalue), new Gray(255));

            Train train1 = new Train();
            Image<Hsv, Byte> hsv1 = new Image<Hsv, byte>(gray.Width, gray.Height);
            Image<Hsv, Byte> hsv2 = hsv1.Copy();
            Rectangle[] rect1 = new Rectangle[3];       // tại sao khai báo mảng 3 hình chữ nhật
            Rectangle rect2 = new Rectangle(0, 0, gray.Width, gray.Height);

            // dùng hàm DrawContourSquare vẽ biên 
            train1.DrawContourCapsule(gray2, hsv1, out hsv2, 1, out rect1, rect2, rect2);

            sta_size = false;

            if (rect1[1].Width != 0)
            {
                sta_shape = true;

                if (rect1[1].Width > data.TabletHeight * (100 - tolerance_size) / 100)
                {
                    if (rect1[1].Height > data.TabletWidth * (100 - tolerance_size) / 100)
                    {
                        sta_size = true;
                    }
                }
            }
            else
            {
                sta_shape = false;
            }
            rect_square = rect1[1];

            CheckArea(gray_bin, out sta_area, data, tolerance_area);


        }
        //public void checkEllipse(Image<Gray, Byte> _grayImage2,ThongTinAnh data,out bool true_shape, out bool true_size,out int ar ,int tolerance_size,int tolerance_area)
        //{
        //    ar = 0;
        //    true_shape = false;
        //    true_size = false;
        // using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
        //        for (
        //            Contour<Point> _contours = _grayImage2.FindContours(
        //                Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_LINK_RUNS, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE);
        //            _contours != null;
        //            _contours = _contours.HNext)
        //        {
        //            Contour<Point> currentContour = _contours.ApproxPoly(_contours.Perimeter*0.05, storage);
        //            if (_contours.Area > data.SoDiemAnh/10 && _contours.Area < data.SoDiemAnh*2)
        //                //only consider contours with area greater than 250
        //            {
        //                if (currentContour.Total > 3 && currentContour.Total < 6)
        //                    //The contour has 3 vertices, it is a triangle
        //                {
                            
        //                    #region determine if all the angles in the contour are within the range of [80, 100] degree

        //                    bool isRectangle = true;
        //                    Point[] pts = currentContour.ToArray();
        //                    LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

        //                    for (int i = 0; i < edges.Length; i++)
        //                    {
        //                        double angle = Math.Abs(edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
        //                        if (angle < 80 || angle > 100)
        //                        {
        //                            isRectangle = false;
        //                            break;
        //                        }
        //                    }

        //                    #endregion

        //                    if (isRectangle == false)
        //                    {
        //                        true_shape = true;
        //                        if (_contours.BoundingRectangle.Width < _contours.BoundingRectangle.Height)
        //                        {
        //                            true_size = true;
        //                        }
        //                        //if (Math.Max(_contours.BoundingRectangle.Width, _contours.BoundingRectangle.Height) >
        //                        //    Math.Max(data.KichThuoc_Dai, data.KichThuoc_Rong)*(100 - tolerance_size)/100 &&
        //                        //    Math.Max(_contours.BoundingRectangle.Width, _contours.BoundingRectangle.Height) <
        //                        //    Math.Max(data.KichThuoc_Dai, data.KichThuoc_Rong)*(100 + tolerance_size)/100)
        //                        //{
        //                        //    if (
        //                        //        Math.Min(_contours.BoundingRectangle.Width, _contours.BoundingRectangle.Height) >
        //                        //        Math.Min(data.KichThuoc_Dai, data.KichThuoc_Rong)*(100 - tolerance_size)/100 &&
        //                        //        Math.Min(_contours.BoundingRectangle.Width, _contours.BoundingRectangle.Height) <
        //                        //        Math.Min(data.KichThuoc_Dai, data.KichThuoc_Rong)*(100 + tolerance_size)/100)
        //                        //    {
        //                        //        true_size = true;
        //                        //    }
                                  
        //                        //}
        //                        else
        //                        {
        //                            true_size =false;
        //                        }

        //                    }
        //                         else
        //                {
        //                    true_shape = false;
        //                }

        //                }
                       
                       
        //            }
        //        }
        //}
        
           
        }
    
}


        
    

